//! # Closure
//! 这是描述外层文件条目（整个包的整体意图）的注释

// # 函数式编程
// 闭包
//  - 可以捕获其所在环境的匿名函数
//  - 是匿名函数 只调用一次

//  - 保存为变量，作为参数  |  函数作为返回值  |  把函数赋值给一个变量，在另一个上下文中调用闭包。为以后执行做铺垫 | 可以其定义的作用域内捕获值

use std::{thread, time::Duration};

// 缓存器
struct Cacher<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    value: Option<u32>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }
    fn value(&mut self, num: u32) -> u32 {
        match self.value {
            Some(value) => value,
            None => {
                let v = (self.calculation)(num);
                self.value = Some(v);
                v
            }
        }
    }
}

fn main() {
    let mut bb = Cacher::new(|x: u32| x);
    println!("{}", bb.value(5));
    let simulated_user_specified_value = 10;
    let simulated_ramdom_number = 7;

    generate_workout(simulated_user_specified_value, simulated_ramdom_number);
}

/**
 * 模拟复杂算法
 */
/// # 这里是新加的注释
fn simulated_expensive_calculation(intensity: u32) -> u32 {
    println!("calculating slowly...");
    // 线程沉睡2s，返回传入的强度
    thread::sleep(Duration::from_secs(2));
    intensity
}

/**
 * 随机运动安排
 * intensity: 运动强度
 * random_number: 随机数字
 */
fn generate_workout(intensity: u32, random_number: u32) {
    // 定义一个闭包
    // 没有定义num的类型，但是由于后面使用的这个闭包，导致类型进行推断后num为u32类型
    let expensive_closure = |num| {
        println!("calculating slowly...");
        // 线程沉睡2s，返回传入的强度
        thread::sleep(Duration::from_secs(2));
        num
    };

    // 以上解决方案还是不够合理，提供以下解决方案
    /*
     * 创建一个struct，她持有闭包以及其调用结果
     *   - 只有在需要结果时才执行该闭包
     *   - 可缓存结果
     */
    // 这个模式通常叫做记忆化(memoization)或者延迟计算(lazy ecaluation)

    // Fn trais 由标准库提供
    // 所有的闭包都至少实现了以下trait之一
    //  - Fn
    //  - FnMut
    //  - FnOnce

    let expensive_result = expensive_closure(intensity);

    if intensity < 25 {
        println!("Today, do {} pushups!", expensive_result);
        println!("Next, do {} situps!", expensive_result)
    } else {
        if random_number == 3 {
            println!("Take abreak today! Remember to stay hydrated!");
        } else {
            println!("Today, run for {} minutes", expensive_result);
        }
    }
}

// 测试
#[cfg(test)]

mod test {
    #[test]
    fn call_with_different_values() {
        let mut c = super::Cacher::new(|a| a);
        let a = c.value(1);// 由于value没有值，所以执行calculation返回1
        let b = c.value(2);// 由于value已经有值了，直接返回1

        assert_eq!(b, 2);
    }
}
